package com.neusoft.databus.policy.service.impl;

import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.neusoft.bizcore.web.exception.CustomRuntimeException;
import com.neusoft.databus.common.constant.DatabusErrorCode;
import com.neusoft.databus.policy.applicationEvent.PolicyBindEvent;
import com.neusoft.databus.policy.applicationEvent.PolicyBindLinkageEvent;
import com.neusoft.databus.policy.applicationEvent.PolicyDeleteEvent;
import com.neusoft.databus.policy.dto.PolicyRefLinkageDTO;
import com.neusoft.databus.policy.model.Expression;
import com.neusoft.databus.policy.model.Policy;
import com.neusoft.databus.policy.model.PolicyRefLinkage;
import com.neusoft.databus.policy.model.PolicyRefResource;
import com.neusoft.databus.policy.repository.ExpressionRepository;
import com.neusoft.databus.policy.repository.PolicyRefResourceRepository;
import com.neusoft.databus.policy.repository.PolicyRepository;
import com.neusoft.databus.policy.service.PolicyService;

@Service
@Transactional(rollbackFor = {Exception.class })
public class PolicyServiceImpl implements PolicyService {
    @Autowired
    private PolicyRepository policyRepository;
    @Autowired
    private ExpressionRepository expressionRepository;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private PolicyRefResourceRepository policyRefResourceRepository;

    @Override
    public Policy findByCode(final String code) {
        return this.policyRepository.findByCode(code);
    }

    @Override
    public List<Policy> findByDisabled(final Boolean disabled) {
        return this.policyRepository.findByDisabled(disabled);
    }

    @Override
    public Policy create(Policy model) {
        model = this.policyRepository.save(model);
        for (final PolicyRefResource policyRefResource : model.getPolicyRefResources()) {
            policyRefResource.setPolicy(model);
            this.policyRefResourceRepository.save(policyRefResource);
        }
        return model;
    }

    @Override
    public void delete(final Policy model) {
        this.policyRepository.delete(model);
        this.policyRefResourceRepository.deleteByPolicyId(model.getId());
    }

    @Override
    public Policy update(Policy model) {
        final Date updateTime = new Date();
        model.setUpdateTime(updateTime);
        model = this.policyRepository.save(model);
        //		for (PolicyRefResource policyRefResource : model.getPolicyRefResources()) {
        //			policyRefResource.setPolicy(model);
        //			policyRefResource.setUpdateTime(updateTime);
        //			policyRefResourceRepository.save(policyRefResource);
        //		}
        return model;
    }

    @Override
    public Policy enable(final Policy model) {
        model.setDisabled(false);
        model.setUpdateTime(new Date());
        this.policyRepository.save(model);
        return model;
    }

    @Override
    public Policy disable(final Policy model) {
        try {
            model.setDisabled(true);
            model.setUpdateTime(new Date());
            this.policyRepository.save(model);
            return model;
        } finally {
            final PolicyDeleteEvent event = new PolicyDeleteEvent(this, model);
            this.applicationContext.publishEvent(event);
        }
    }

    @Override
    public Policy bind(final Long id, final List<Long> expressionIdList) {
        final Policy model = this.policyRepository.getOne(id);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {id });
        }
        model.getExpressions().clear();
        expressionIdList.stream().map(i -> {
            final Expression expression = this.expressionRepository.getOne(i);
            return expression;
        }).filter(e -> null != e).forEach(model.getExpressions()::add);
        model.setUpdateTime(new Date());
        this.policyRepository.save(model);
        return model;
    }

    @Override
    public Policy bind(final String code, final List<String> resourceCodeList) {
        Policy model = this.policyRepository.findByCode(code);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {code });
        }
        // 过滤取消绑定的资源code
        final List<String> oldResourceCodeList = Lists.newArrayList();
        model.getPolicyRefResources().stream().map(r -> {
            return r.getResourceCode();
        }).filter(e -> null != e).forEach(oldResourceCodeList::add);
        final Iterator<String> iterator = oldResourceCodeList.iterator();
        while (iterator.hasNext()) {
            if (resourceCodeList.contains(iterator.next())) {
                iterator.remove();
            }
        }
        model.getPolicyRefResources().clear();
        for (final String resourceCode : resourceCodeList) {
            final PolicyRefResource policyRefResource = new PolicyRefResource();
            policyRefResource.setResourceCode(resourceCode);
            policyRefResource.setPolicy(model);
            model.getPolicyRefResources().add(policyRefResource);
        }
        model.setUpdateTime(new Date());
        model = this.policyRepository.save(model);
        if (oldResourceCodeList.size() > 0) {
            final PolicyBindEvent event = new PolicyBindEvent(this, oldResourceCodeList);
            this.applicationContext.publishEvent(event);
        }
        return model;
    }

    @Override
    public Policy bindLinkage(final String code, final List<PolicyRefLinkageDTO> policyRefLinkageDTOs) {
        Policy model = this.policyRepository.findByCode(code);
        if (null == model) {
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_404001, new Object[] {code });
        }
        // 过滤取消绑定的资源code
        final List<String> oldResourceCodeList = Lists.newArrayList();
        model.getPolicyRefLinkages().stream().map(r -> {
            return r.getResourceCode();
        }).filter(e -> null != e).forEach(oldResourceCodeList::add);

        final List<String> resourceCodeList =
                policyRefLinkageDTOs.stream().map(it -> it.getResourceCode()).collect(Collectors.toList());

        final Iterator<String> iterator = oldResourceCodeList.iterator();
        while (iterator.hasNext()) {
            if (resourceCodeList.contains(iterator.next())) {
                iterator.remove();
            }
        }
        model.getPolicyRefLinkages().clear();
        for (final PolicyRefLinkageDTO dto : policyRefLinkageDTOs) {
            final PolicyRefLinkage policyRefLinkage = new PolicyRefLinkage();
            policyRefLinkage.setResourceCode(dto.getResourceCode());
            policyRefLinkage.setControlUsageMetrics(dto.getControlUsageMetrics());
            policyRefLinkage.setCancelUsageMetrics(dto.getCancelUsageMetrics());
            policyRefLinkage.setPolicy(model);
            model.getPolicyRefLinkages().add(policyRefLinkage);
        }
        model.setUpdateTime(new Date());
        model = this.policyRepository.save(model);
        if (oldResourceCodeList.size() > 0) {
            final PolicyBindLinkageEvent event = new PolicyBindLinkageEvent(this, oldResourceCodeList);
            this.applicationContext.publishEvent(event);
        }
        return model;
    }
}
